import ogre.renderer.OGRE as ogre
import numpy as np
import matplotlib.pyplot as plt
import math
from mpl_toolkits.mplot3d import Axes3D

#CLASS////////////////////////////////////////////////////////////////
class Led:
	####################
	class Link:
		POV = None
	####################
	def __init__(self, Link, Pos, Size, Rotated, DrivedByChsN):
		self.Pos = Pos
		self.Size = ogre.Vector2(Size.x,Size.y)
		self.DrivedByChsN = DrivedByChsN

		if Rotated:
			self.Size.x, self.Size.y = self.Size.y, self.Size.x
		return
	####################
	def GetPathLenght(self):
		return 2*math.pi*abs(self.Pos.x)
	####################
	def GetLightCurrent(self):
		return self.Link.POV.BrightCurr * abs(self.Pos.x/self.Link.POV.Leds[0][0].Pos.x) #oslabienie swiecenia ze wzgledu na zblizanie do srodka
	####################
	def GetCurrentPerDrivenChannel(self):
		return self.GetPathLenght() / self.DrivedByChsN
	####################
	def Plot(self):
		Left1 = [self.Pos.x-self.Size.x/2, self.Pos.x-self.Size.x/2]
		Left2 = [self.Pos.y-self.Size.y/2, self.Pos.y+self.Size.y/2]

		Up1 = [self.Pos.x-self.Size.x/2, self.Pos.x+self.Size.x/2]
		Up2 = [self.Pos.y+self.Size.y/2, self.Pos.y+self.Size.y/2]

		Right1 = [self.Pos.x+self.Size.x/2, self.Pos.x+self.Size.x/2]
		Right2 = [self.Pos.y+self.Size.y/2, self.Pos.y-self.Size.y/2]

		Down1 = [self.Pos.x+self.Size.x/2, self.Pos.x-self.Size.x/2]
		Down2 = [self.Pos.y-self.Size.y/2, self.Pos.y-self.Size.y/2]

		plt.plot( Left1, Left2, 'k-', lw = 1 )
		plt.plot( Up1, Up2, 'k-', lw = 1 )
		plt.plot( Right1, Right2, 'k-', lw = 1 )
		plt.plot( Down1, Down2, 'k-', lw = 1 )
		return
	####################

	Pos = ogre.Vector2
	Size = ogre.Vector3
	Link = Link
	DrivedByChsN = 0
#CLASS////////////////////////////////////////////////////////////////
class Row:
	####################
	class Linkage:
		POV = None
	####################
	def __init__(self, Link, Number, AddChannels = 0):
		self.Leds = []
		self.Link = Link
		self.Number = Number
		self.AddChannels = AddChannels
		return
	#####################
	def AddLed(self, Led):
		Led.DrivedByChsN += self.AddChannels
		self.Leds.append(Led)
		return
	####################
	def GetMaxCurrent(self):
		MaxCurr = 0
		for Led in self.Leds:
			RowCurr = Led.GetLightCurrent() * self.Link.POV.ColsN  / Led.DrivedByChsN 
			if( MaxCurr < RowCurr ):
				MaxCurr = RowCurr
		
		return MaxCurr
	####################
	def GetAverCurrent(self):
		AvCurr = 0
		CurrentInt = 0
		for Led in self.Leds:
			PrescCurr = Led.GetLightCurrent() * self.Link.POV.ColsN / Led.DrivedByChsN  #By multiplexing 
			CurrentInt += PrescCurr
		
		AvCurr = CurrentInt / self.Link.POV.ColsN
		return AvCurr
	#####################
	
	Leds = None
	Number = 0
	Link = None
	AddChannels = 0
#CLASS////////////////////////////////////////////////////////////////
class Col:
	####################
	class Linkage:
		POV = None
	####################
	def __init__(self, Link, Number, AddChannels = 0):
		self.Leds = []
		self.Link = Link
		self.Number = Number
		self.AddChannels = AddChannels
		return
	#####################
	def AddLed(self, Led):
		Led.DrivedByChsN += self.AddChannels
		self.Leds.append(Led)
		return
	####################

	Leds = None
	Number = 0
	Link = None
	AddChannels = 0
#CLASS////////////////////////////////////////////////////////////////
class POV:
	 ####################
	def __init__(self):
		self.D = self.LedSpacing * self.LedRes + self.LedSpacing
		self._CreateLeds()

		return
	#####################
	def PrintInfo(self):
		print( "Diameter: {0}cm".format( self.D *1e2 ) )
		print( "LedSpacing: {0}mm".format( self.LedSpacing *1e3 ) )
		print( "TotalLedCount: {0}".format( self.GetTotalLedCount() ) )
		print( "ChannelsLeft: {0}".format( self.GetChannelsLeft() ) )

		return 
	#####################
	def IsLedOutMarigin( self, Led ):
		Point = ogre.Vector2(0,0)
		Point.y = Led.Pos.y + Led.Size.y/2
		if Led.Pos.x < 0.0:
			Point.x = Led.Pos.x - Led.Size.x/2
		else:
			Point.x = Led.Pos.x + Led.Size.x/2

		IsInside = Point.length() > (self.D/2-self.MarginSize)
		return IsInside
	#####################
	def GetTotalLedCount(self):
		N = 0
		for Col in self.Leds:
			N += len(Col)
		return N
	#####################
	def PrintLedsCountInColumns(self):
		for Col in self.Leds:
			print len(Col)
	#####################
	def PlotDisplay(self):
		N = self.GetTotalLedCount()
		PX = np.zeros( N )
		PY = np.zeros( N )

		plt.figure()

		I = 0
		for Col in self.Leds:
			for Led in Col:
				PX[I] = Led.Pos.x
				PY[I] = Led.Pos.y
				I += 1
				Led.Plot()

		self._PrintContour()
		self._PrintMargin()
		plt.plot( PX, PY, 'ro' )

		plt.show()
		return
	#####################
	def DrawCurrentsInCols(self):
		N = len(self.Leds)

		C = np.zeros( N )
		I = np.zeros( N )

		IN = 0
		for Col in self.Leds:
			C[IN] = Col[0].Pos.x
			I[IN] = Col[0].GetLightCurrent()*1e3
			IN += 1

		CX = self._GetDisplayPcbContour()[0]
		CY = (self._GetDisplayPcbContour()[1]/np.max(self._GetDisplayPcbContour()[1]))*np.max(Col[0].GetLightCurrent()*1e3)

		plt.figure()
		plt.plot( C, I, 'b' )
		plt.plot( CX, CY, 'r' )
		plt.show()

		return
	#####################
	def DrawLedsCurrents(self):
		N = self.GetTotalLedCount()
		
		Curr = np.zeros( [self.ColsN,self.RowsN] )

		fig = plt.figure()

		I = 0
		for Col in range( 0, self.ColsN ):
			for Row in range( 0, self.RowsN ):
				if( Row < len(self.Cols[Col].Leds) ):
					Curr[Col][Row] = self.Cols[Col].Leds[Row].GetCurrentPerDrivenChannel()*1e3
				else:
					Curr[Col][Row]
				#Led.Plot()

		#self._PrintContour()
		#self._PrintMargin()

		# the x distribution will be centered at -1, the y distro
		#plt.imshow(Curr,nt,interpolation='nearest',origin='lower')

		Im = plt.imshow(np.rot90(Curr), interpolation="nearest" )
		fig.colorbar(Im) 

		plt.show()

		return
	#####################
	def GetChannelsLeft(self):
		MiddleColN = int(len(self.Leds)/2)
		return self._GetChannelsLeftInCol(self.Leds[MiddleColN])
	#####################
	def PrintCurrsInRows(self):
		MiddleColN = int(len(self.Leds)/2)
		Rows = len(self.Leds[MiddleColN])

		IN = 0
		print("\nCurrent in Rows")
		for Row in self.Rows:
			print( "{0}) Cols({3}) Av:{1}mA Max:{2}mA".format( IN, Row.GetAverCurrent() * 1e3, Row.GetMaxCurrent() * 1e3, len(Row.Leds) ) )
			IN += 1
		return
	#####################
	def _CreateLeds(self):
		XN = 0
		for X in np.linspace( -self.D/2, self.D/2, self.LedRes ):
			self.Leds.append([])
			YN = 0
			Rotated = (XN + 1) % 2
			for Y in np.linspace( self.DownMargin + self.LedSize.y/2, self.D/2, self.LedRes/2 ):
				Pos = ogre.Vector2( X, Y )
	
				LedLink = Led.Link
				LedLink.POV = self

				Channels = 1

				NewLed = Led( LedLink, Pos, self.LedSize, Rotated, Channels )

				if( self.IsLedOutMarigin( NewLed ) ):
					break

				self.Leds[XN].append( NewLed )
				YN += 1
				if self.RowsN < YN:
					self.RowsN = YN
				Rotated = not Rotated
			if len(self.Leds[XN]) == 0:
				self.Leds.pop()
			else:
				XN += 1
		self.ColsN = XN

		self.AssingLedsToColsAndRows()
		return
	#####################
	def AssingLedsToColsAndRows(self):

		for RowN in range(0, self.RowsN):
			AddChannels = 0
			if( RowN == 0 ):
				AddChannels = 1

			if( RowN == 1 ):
				AddChannels = 1

			if( RowN == 2 ):
				AddChannels = 1

			if( RowN == 3 ):
				AddChannels = 1

			if( RowN == 4 ):
				AddChannels = 1

			if( RowN == 5 ):
				AddChannels = 1

			if( RowN == 6 ):
				AddChannels = 1

			if( RowN == 7 ):
				AddChannels = 1

			if( RowN == 8 ):
				AddChannels = 1

			if( RowN == 9 ):
				AddChannels = 1

			if( RowN == 10 ):
				AddChannels = 1

			Link = Row.Linkage()
			Link.POV = self
			NewRow = Row(Link,RowN,AddChannels)
			for ColN in range(0, self.ColsN):
				if( RowN >= len( self.Leds[ColN] )):
					continue
				Led = self.Leds[ColN][RowN]
				NewRow.AddLed(Led)
			self.Rows.append( NewRow )

		for ColN in range(0, self.ColsN):
			AddChannels = 0
			#AddChannels = math.floor(self.LedDriverChs * self.LedDriversN / len(self.Leds[ColN]) - 1.0)

			Link = Col.Linkage()
			Link.POV = self
			NewCol = Col(Link,ColN,AddChannels)
			for Led in self.Leds[ColN]:
				NewCol.AddLed(Led)
			self.Cols.append( NewCol )
	
		return
	#####################
	def _PrintContour(self):
		CN = len(self.Leds) * 10
		CX = np.zeros( CN )
		CY = np.zeros( CN )

		IN = 0
		for X in np.linspace( -self.PcbD/2, self.PcbD/2, CN ):
			CX[IN] = X
			CY[IN] = math.sqrt((self.PcbD/2)**2 - X**2)
			IN += 1

		plt.plot( CX, CY, 'r' )
		return
	#####################
	def _GetDisplayPcbContour(self):
		CN = len(self.Leds) * 10
		CX = np.zeros( CN )
		CY = np.zeros( CN )

		IN = 0
		for X in np.linspace( -self.PcbD/2, self.PcbD/2, CN ):
			CX[IN] = X
			CY[IN] = math.sqrt((self.PcbD/2)**2 - X**2)
			IN += 1

		plt.plot( CX, CY, 'r' )
		return [CX,CY]
	#####################
	def _PrintMargin(self):
		CN = len(self.Leds) * 10
		CX = np.zeros( CN )
		CY = np.zeros( CN )

		R = self.PcbD/2 - self.MarginSize

		IN = 0
		for X in np.linspace( -R, R, CN ):
			CX[IN] = X
			CY[IN] = math.sqrt((R)**2 - X**2)
			IN += 1

		plt.plot( CX, CY, 'r' )
		return
	#####################
	def _GetChannelsLeftInCol(self,Col):
		UsedChs = 0
		for Led in Col:
			UsedChs += Led.DrivedByChsN

		Left = self.LedDriverChs*self.LedDriversN - UsedChs
		return Left
	#####################
	#####################

	PcbD = 116e-3
	D = 0.0
	LedSpacing = 2.5e-3
	LedRes = 45
	Leds = []
	Rows = []
	Cols = []
	LedSize = ogre.Vector2(2.0e-3, 1.25e-3)
	MarginSize = 1.0e-3
	DownMargin = 2.0e-3
	BrightCurr = 9e-3 #Prad w najbardziej zewnetrznej diodzie
	LedDriverChs = 16
	LedDriversN = 2
	RowsN = 0
	ColsN = 0
#CLASS////////////////////////////////////////////////////////////////


POV = POV()

POV.PrintInfo()
POV.PrintCurrsInRows()
POV.PrintLedsCountInColumns()
POV.DrawLedsCurrents()
POV.DrawCurrentsInCols()
POV.PlotDisplay()